package com.aizuda.boot.modules.business.contract.service.impl;

import cn.hutool.core.io.IoUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.aizuda.boot.modules.auth.domain.entity.ThirdUser;
import com.aizuda.boot.modules.auth.domain.entity.ThirdUserAuth;
import com.aizuda.boot.modules.auth.mapper.ThirdUserMapper;
import com.aizuda.boot.modules.auth.mapper.ThirdUserAuthMapper;
import com.aizuda.boot.modules.business.feishu.strategy.FeishuConfigStrategy;
import com.aizuda.boot.config.properties.FeiShuConfigProperties;
import com.aizuda.boot.modules.file.service.IFileInfoService;
import com.aizuda.boot.modules.file.domain.vo.FileUploadVO;
import com.aizuda.boot.modules.business.contract.dto.SingleResponse;
import com.aizuda.boot.modules.business.contract.util.AuthUtil;
import com.aizuda.boot.modules.business.contract.dto.UserInfo;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.concurrent.ThreadPoolExecutor;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import com.aizuda.boot.modules.business.contract.entity.dto.ContractExportReqDTO;
import com.aizuda.boot.modules.business.contract.entity.ContractFieldEntity;
import com.aizuda.boot.modules.business.contract.entity.ContractFieldValueEntity;
import com.aizuda.boot.modules.business.contract.entity.ContractTypeEntity;
import com.aizuda.boot.modules.business.contract.mapper.ContractFieldMapper;
import com.aizuda.boot.modules.business.contract.mapper.ContractFieldValueMapper;
import com.aizuda.boot.modules.business.performance.mapper.ContractTypeMapper;
import com.aizuda.boot.modules.business.contract.service.ContractExportService;
import com.aizuda.boot.modules.business.counterparty.entity.CounterpartyInfoEntity;
import com.aizuda.boot.modules.business.counterparty.entity.LegalEntityDetailEntity;
import com.aizuda.boot.modules.business.counterparty.mapper.CounterpartyInfoMapper;
import com.aizuda.boot.modules.business.counterparty.mapper.LegalEntityDetailMapper;
import com.aizuda.boot.modules.system.entity.SysUser;
import com.aizuda.boot.modules.system.mapper.SysUserMapper;
import com.aizuda.boot.modules.file.domain.entity.FileInfo;
import com.aizuda.boot.modules.file.mapper.FileInfoMapper;
import com.aizuda.boot.modules.common.constant.enums.FieldTypeEnum;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 合同导出服务实现类
 * 
 * @author yangshen
 * @date 2025/08/08
 */
@Slf4j
@Service
public class ContractExportServiceImpl implements ContractExportService {

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;
    
    @Autowired
    private ContractFieldMapper contractFieldMapper;
    
    @Autowired
    private ContractFieldValueMapper contractFieldValueMapper;
    
    @Autowired
    private SysUserMapper sysUserMapper;
    
    @Autowired
    private FileInfoMapper fileInfoMapper;
    
    @Autowired
    private CounterpartyInfoMapper counterpartyInfoMapper;
    
    @Autowired
    private LegalEntityDetailMapper legalEntityDetailMapper;
    
    @Autowired
    private ContractTypeMapper contractTypeMapper;
    
    @Autowired
    private FeishuConfigStrategy feishuConfigStrategy;
    
    @Autowired
    private ThirdUserMapper thirdUserMapper;
    
    @Autowired
    private ThirdUserAuthMapper thirdUserAuthMapper;
    
    @Autowired
    private IFileInfoService fileInfoService;

    @Override
    public Boolean exportContractsAsync(ContractExportReqDTO dto) {
        try {
            UserInfo currentUser = AuthUtil.getCurrentUser();
            
            // 发送开始导出的飞书卡片通知
            sendContractExportProgressCard(
                currentUser.getId(),
                currentUser.getNickName(),
                dto.getContractIds().size()
            );
            
            // 异步执行导出
            threadPoolExecutor.execute(() -> {
                try {
                    exportContracts(dto, currentUser);
                } catch (Exception e) {
                    log.error("异步导出合同失败", e);
                    // 发送失败通知
                    sendContractExportErrorCard(
                        currentUser.getId(),
                        currentUser.getNickName(),
                        "导出过程中发生错误，请联系管理员"
                    );
                }
            });
            
            return true;
        } catch (Exception e) {
            log.error("启动异步导出失败", e);
            return false;
        }
    }
    
    /**
     * 执行合同导出
     */
    private void exportContracts(ContractExportReqDTO dto, UserInfo currentUser) {
        try {
            log.info("开始导出合同，合同数量：{}，字段数量：{}", dto.getContractIds().size(), dto.getFieldIds().size());
            
            // 1. 查询字段信息
            List<ContractFieldEntity> fields = contractFieldMapper.selectBatchIds(dto.getFieldIds());
            Map<Long, ContractFieldEntity> fieldMap = fields.stream()
                    .collect(Collectors.toMap(ContractFieldEntity::getId, f -> f));
            
            // 2. 查询合同字段值
            LambdaQueryWrapper<ContractFieldValueEntity> valueQuery = new LambdaQueryWrapper<>();
            valueQuery.in(ContractFieldValueEntity::getContractId, dto.getContractIds())
                     .in(ContractFieldValueEntity::getFieldId, dto.getFieldIds());
            List<ContractFieldValueEntity> fieldValues = contractFieldValueMapper.selectList(valueQuery);
            
            // 3. 按合同ID分组字段值
            Map<Integer, List<ContractFieldValueEntity>> contractFieldValuesMap = fieldValues.stream()
                    .collect(Collectors.groupingBy(ContractFieldValueEntity::getContractId));
            
            // 4. 生成Excel文件
            String fileName = generateExcelFile(dto, fieldMap, contractFieldValuesMap);
            
            // 5. 上传文件到系统并获取下载URL
            String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
            String finalFileName = String.format("合同台账%s.xlsx", timestamp);
            
            // 上传文件到文件服务并获取下载URL
            String fileUrl = uploadFileAndGetUrl(fileName, finalFileName, currentUser);
            
            // 发送导出完成的飞书卡片通知
            sendContractExportCompleteCard(
                currentUser.getId(),
                currentUser.getNickName(),
                dto.getContractIds().size(),
                finalFileName,
                fileUrl
            );
            
            log.info("合同导出完成，文件：{}", fileName);
            
        } catch (Exception e) {
            log.error("导出合同失败", e);
            throw new RuntimeException("导出合同失败: " + e.getMessage());
        }
    }
    
    /**
     * 生成Excel文件
     */
    private String generateExcelFile(ContractExportReqDTO dto, Map<Long, ContractFieldEntity> fieldMap, 
                                   Map<Integer, List<ContractFieldValueEntity>> contractFieldValuesMap) throws IOException {
        
        String timestamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        
        // 保存到项目的uploads/contract_exports目录
        String uploadsDir = "uploads" + File.separator + "contract_exports";
        String fileName = uploadsDir + File.separator + "contract_export_" + timestamp + ".xlsx";
        
        log.info("保存Excel文件到项目目录: {}", fileName);
        
        // 确保目录存在
        File parentDir = new File(uploadsDir);
        if (!parentDir.exists()) {
            boolean created = parentDir.mkdirs();
            log.info("创建合同导出目录 {}: {}", uploadsDir, created);
        }
        
        ExcelWriter writer = ExcelUtil.getWriter();
        
        try {
            // 写入表头
            List<String> headers = new ArrayList<>();
            headers.add("合同ID");
            
            // 添加字段表头
            for (Long fieldId : dto.getFieldIds()) {
                        ContractFieldEntity field = fieldMap.get(fieldId);
                if (field != null) {
                    headers.add(field.getFieldName());
                }
            }
            
            writer.writeHeadRow(headers);
            
            // 写入数据行
            int rowIndex = 1;
            for (Integer contractId : dto.getContractIds()) {
                List<Object> rowData = new ArrayList<>();
                rowData.add(contractId);
                
                List<ContractFieldValueEntity> contractFieldValues = contractFieldValuesMap.getOrDefault(contractId, new ArrayList<>());
                Map<Long, ContractFieldValueEntity> fieldValueMap = contractFieldValues.stream()
                        .collect(Collectors.toMap(ContractFieldValueEntity::getFieldId, v -> v, (v1, v2) -> v1));
                
                // 按字段顺序添加值
                for (Long fieldId : dto.getFieldIds()) {
                    ContractFieldEntity field = fieldMap.get(fieldId);
                    ContractFieldValueEntity value = fieldValueMap.get(fieldId);
                    
                    String displayValue = convertFieldValue(field, value);
                    rowData.add(displayValue);
                }
                
                writer.writeRow(rowData);
                rowIndex++;
            }
            
            // 写入文件
            writer.flush(new File(fileName));
            
            return fileName;
            
        } catch (Exception e) {
            IoUtil.close(writer);
            throw e;
        }
    }
    
    /**
     * 转换字段值显示
     */
    private String convertFieldValue(ContractFieldEntity field, ContractFieldValueEntity value) {
        if (field == null || value == null || !StringUtils.hasText(value.getFieldValue())) {
            return "";
        }
        
        String fieldValue = value.getFieldValue();
        String fieldType = field.getFieldType();
        
        try {
            switch (FieldTypeEnum.valueOf(fieldType)) {
                case PERSON:
                    return convertPersonValue(fieldValue);
                    
                case ATTACHMENT:
                    return convertAttachmentValue(fieldValue);
                    
                case SINGLE_SELECT:
                case MULTI_SELECT:
                case DROPDOWN_SINGLE:
                case DROPDOWN_MULTI:
                case TREE_SINGLE:
                case TREE_MULTI:
                    return convertSelectValue(fieldValue, field.getFieldOptions());
                    
                case COUNTERPART_PEER:
                    return convertCounterpartPeerValue(fieldValue);
                    
                case COUNTERPART_SELF:
                    return convertCounterpartSelfValue(fieldValue);
                    
                case CONTRACT_TYPE:
                    return convertContractTypeValue(fieldValue);
                    
                default:
                    return fieldValue;
            }
        } catch (Exception e) {
            log.warn("转换字段值失败，fieldType: {}, fieldValue: {}, error: {}", fieldType, fieldValue, e.getMessage());
            return fieldValue;
        }
    }
    
    /**
     * 转换人员字段值
     */
    private String convertPersonValue(String fieldValue) {
        try {
            Long userId = Long.valueOf(fieldValue);
            SysUser user = sysUserMapper.selectById(userId);
            return user != null ? user.getRealName() : fieldValue;
        } catch (Exception e) {
            log.warn("转换人员字段值失败: {}", fieldValue);
            return fieldValue;
        }
    }
    
    /**
     * 转换附件字段值
     */
    private String convertAttachmentValue(String fieldValue) {
        try {
            Long fileId = Long.valueOf(fieldValue);
            FileInfo fileInfo = fileInfoMapper.selectById(fileId);
            return fileInfo != null ? fileInfo.getFileName() : fieldValue;
        } catch (Exception e) {
            log.warn("转换附件字段值失败: {}", fieldValue);
            return fieldValue;
        }
    }
    
    /**
     * 转换选择类型字段值
     */
    private String convertSelectValue(String fieldValue, String fieldOptions) {
        if (!StringUtils.hasText(fieldOptions)) {
            return fieldValue;
        }
        
        try {
            JSONObject optionsJson = JSONUtil.parseObj(fieldOptions);
            List<Map<String, Object>> options = (List<Map<String, Object>>) optionsJson.get("options");
            
            if (options == null || options.isEmpty()) {
                return fieldValue;
            }
            
            // 支持多选值（逗号分隔）
                String[] keys = fieldValue.split(",");
            List<String> displayValues = new ArrayList<>();
                
                for (String key : keys) {
                String trimmedKey = key.trim();
                for (Map<String, Object> option : options) {
                    if (trimmedKey.equals(String.valueOf(option.get("key")))) {
                        displayValues.add(String.valueOf(option.get("value")));
                            break;
                        }
                    }
                }
                
            return displayValues.isEmpty() ? fieldValue : String.join(",", displayValues);
        } catch (Exception e) {
            log.warn("转换选择字段值失败: {}", fieldValue);
        return fieldValue;
        }
    }
    
    /**
     * 转换对方字段值
     */
    private String convertCounterpartPeerValue(String fieldValue) {
        try {
            Long counterpartyId = Long.valueOf(fieldValue);
            CounterpartyInfoEntity counterparty = counterpartyInfoMapper.selectById(counterpartyId);
            return counterparty != null ? counterparty.getCounterpartyName() : fieldValue;
        } catch (Exception e) {
            log.warn("转换对方字段值失败: {}", fieldValue);
            return fieldValue;
        }
    }
    
    /**
     * 转换我方字段值
     */
    private String convertCounterpartSelfValue(String fieldValue) {
        try {
            Long legalEntityId = Long.valueOf(fieldValue);
            LegalEntityDetailEntity legalEntity = legalEntityDetailMapper.selectById(legalEntityId);
            return legalEntity != null ? legalEntity.getLegalEntityName() : fieldValue;
        } catch (Exception e) {
            log.warn("转换我方字段值失败: {}", fieldValue);
            return fieldValue;
        }
    }
    
    /**
     * 转换合同类型字段值
     */
    private String convertContractTypeValue(String fieldValue) {
        try {
            Long contractTypeId = Long.valueOf(fieldValue);
            ContractTypeEntity contractType = contractTypeMapper.selectById(contractTypeId);
            return contractType != null ? contractType.getTypeName() : fieldValue;
        } catch (Exception e) {
            log.warn("转换合同类型字段值失败: {}", fieldValue);
            return fieldValue;
        }
    }
    
    /**
     * 上传文件到文件服务并获取下载URL
     */
    private String uploadFileAndGetUrl(String fileName, String finalFileName, UserInfo currentUser) {
        try {
            // 读取文件
            File sourceFile = new File(fileName);
            log.info("检查文件: 路径=[{}], 存在=[{}], 大小=[{}]", fileName, sourceFile.exists(), sourceFile.length());
            
            if (!sourceFile.exists()) {
                log.error("文件不存在: {}", fileName);
                return null;
            }
            
            log.info("开始通过文件上传接口处理文件");
            
            // 创建MultipartFile对象，包装现有的文件
            MultipartFile multipartFile = createMultipartFile(sourceFile, finalFileName);
            
            // 调用文件上传接口 (文件夹类型: 2=合同文件夹)
            SingleResponse<FileUploadVO> uploadResult = fileInfoService.fileUpload(multipartFile, 2, currentUser);
            
            // 详细记录上传结果
            log.info("文件上传响应: success=[{}], message=[{}], data=[{}]", 
                    uploadResult.isSuccess(), uploadResult.getMessage(), uploadResult.getData());
            
            if (uploadResult.isSuccess() && uploadResult.getData() != null) {
                FileUploadVO uploadVO = uploadResult.getData();
                
                // 详细记录返回的数据
                log.info("FileUploadVO详细信息: fileId=[{}], fileName=[{}], fileKey=[{}], fileUrl=[{}], fileSize=[{}], fileType=[{}]",
                        uploadVO.getFileId(), uploadVO.getFileName(), uploadVO.getFileKey(), 
                        uploadVO.getFileUrl(), uploadVO.getFileSize(), uploadVO.getFileType());
                
                String fileUrl = uploadVO.getFileUrl();
                String fileKey = uploadVO.getFileKey();
                
                // 如果fileUrl为空，尝试通过fileKey生成URL
                if (fileUrl == null || fileUrl.isEmpty()) {
                    log.warn("fileUrl为空，尝试通过fileKey生成URL: fileKey=[{}]", fileKey);
                    if (fileKey != null && !fileKey.isEmpty()) {
                        // 通过fileInfoService的generateFileUrl方法生成URL
                        fileUrl = fileInfoService.generateFileUrl(fileKey);
                        log.info("通过fileKey生成的URL: [{}]", fileUrl);
                    }
                }
                
                log.info("最终使用的fileUrl: [{}]", fileUrl);
                
                // 删除临时文件
                if (sourceFile.delete()) {
                    log.info("临时文件删除成功: {}", fileName);
                } else {
                    log.warn("临时文件删除失败: {}", fileName);
                }
                
                return fileUrl;
            } else {
                log.error("文件上传失败: success=[{}], message=[{}]", uploadResult.isSuccess(), uploadResult.getMessage());
                return null;
            }
            
        } catch (Exception e) {
            log.error("文件处理失败", e);
            return null;
        }
    }
    
    /**
     * 创建MultipartFile对象
     */
    private MultipartFile createMultipartFile(File file, String fileName) throws IOException {
        return new MultipartFile() {
            @Override
            public String getName() {
                return "file";
            }

            @Override
            public String getOriginalFilename() {
                return fileName;
            }

            @Override
            public String getContentType() {
                return "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
            }

            @Override
            public boolean isEmpty() {
                return file.length() == 0;
            }

            @Override
            public long getSize() {
                return file.length();
            }

            @Override
            public byte[] getBytes() throws IOException {
                return IoUtil.readBytes(new FileInputStream(file));
            }

            @Override
            public java.io.InputStream getInputStream() throws IOException {
                return new FileInputStream(file);
            }

            @Override
            public void transferTo(File dest) throws IOException, IllegalStateException {
                log.info("开始transferTo: 源文件=[{}], 目标文件=[{}]", file.getAbsolutePath(), dest.getAbsolutePath());
                
                // 确保目标目录存在
                File parentDir = dest.getParentFile();
                if (parentDir != null) {
                    log.info("检查目标目录: 路径=[{}], 存在=[{}], 可写=[{}]", 
                            parentDir.getAbsolutePath(), parentDir.exists(), parentDir.canWrite());
                    
                    if (!parentDir.exists()) {
                        boolean created = parentDir.mkdirs();
                        log.info("创建目标目录 {}: {}", parentDir.getPath(), created);
                        if (!created) {
                            throw new IOException("无法创建目标目录: " + parentDir.getAbsolutePath());
                        }
                    }
                    
                    if (!parentDir.canWrite()) {
                        throw new IOException("目标目录不可写: " + parentDir.getAbsolutePath());
                    }
                }
                
                try (FileInputStream fis = new FileInputStream(file);
                     FileOutputStream fos = new FileOutputStream(dest)) {
                    IoUtil.copy(fis, fos);
                    log.info("文件复制成功: {} -> {}", file.getAbsolutePath(), dest.getAbsolutePath());
                } catch (IOException e) {
                    log.error("文件复制失败: {} -> {}, 错误: {}", file.getAbsolutePath(), dest.getAbsolutePath(), e.getMessage());
                    throw e;
                }
            }
        };
    }
    
    /**
     * 发送合同导出进度卡片
     */
    private void sendContractExportProgressCard(Long userId, String userName, int contractCount) {
        try {
            // 通过ThirdUserAuth表查询用户绑定的第三方账户
            List<ThirdUser> thirdUsers = getThirdUsersByUserId(userId);
            
            if (thirdUsers.isEmpty()) {
                log.warn("用户 {} 没有绑定第三方账户，无法发送飞书通知", userName);
                return;
            }
            
            // 向所有绑定的第三方账户发送通知
            for (ThirdUser thirdUser : thirdUsers) {
                // 获取配置中的进度卡片模板ID
                String templateId = getContractExportTemplateId(thirdUser.getTenantKey(), "contract-export-progress");
                if (templateId != null) {
                    sendFeishuCard(
                        thirdUser.getOpenId(),
                        thirdUser.getTenantKey(),
                        templateId,
                contractCount,
                        null,
                        null
                );
            } else {
                    log.warn("租户 {} 未配置合同导出进度卡片模板", thirdUser.getTenantKey());
                }
            }
            
        } catch (Exception e) {
            log.error("发送合同导出进度卡片失败", e);
        }
    }
    
    /**
     * 发送合同导出完成卡片
     */
    private void sendContractExportCompleteCard(Long userId, String userName, int contractCount, String fileName, String fileUrl) {
        try {
            // 通过ThirdUserAuth表查询用户绑定的第三方账户
            List<ThirdUser> thirdUsers = getThirdUsersByUserId(userId);
            
            if (thirdUsers.isEmpty()) {
                log.warn("用户 {} 没有绑定第三方账户，无法发送飞书通知", userName);
                return;
            }
            
            // 向所有绑定的第三方账户发送通知
            for (ThirdUser thirdUser : thirdUsers) {
                // 获取配置中的完成卡片模板ID
                String templateId = getContractExportTemplateId(thirdUser.getTenantKey(), "contract-export-complete");
                if (templateId != null) {
                    log.info("合同导出 - 发送完成卡片: 用户=[{}], 合同数=[{}], 文件名=[{}], 文件URL=[{}]", 
                            userName, contractCount, fileName, fileUrl);
                    sendFeishuCard(
                        thirdUser.getOpenId(),
                        thirdUser.getTenantKey(),
                        templateId,
                contractCount,
                        fileName,
                        fileUrl
                );
            } else {
                    log.warn("租户 {} 未配置合同导出完成卡片模板", thirdUser.getTenantKey());
                }
            }
            
        } catch (Exception e) {
            log.error("发送合同导出完成卡片失败", e);
        }
    }
    
    /**
     * 发送合同导出错误卡片
     */
    private void sendContractExportErrorCard(Long userId, String userName, String errorMessage) {
        try {
            log.error("合同导出失败，用户: {}, 错误: {}", userName, errorMessage);
            // 这里可以实现错误通知逻辑
        } catch (Exception e) {
            log.error("发送合同导出错误卡片失败", e);
        }
    }
    
    /**
     * 发送飞书卡片通用方法
     */
    private void sendFeishuCard(String openId, String tenantKey, String cardId, 
                              Integer contractNum, String fileName, String fileUrl) {
        try {
            // 获取飞书配置
            FeiShuConfigProperties.FeiShuAppConfig config = feishuConfigStrategy.getConfigByTenantKey(tenantKey);
            if (config == null) {
                log.warn("未找到租户 {} 的飞书配置", tenantKey);
                return;
            }
            
            // 构建卡片内容 - 根据用户需求使用指定的卡片ID
            String cardContent = buildCardContent(cardId, contractNum, fileName, fileUrl);
            
            // 发送卡片消息
            sendCardMessage(config, openId, cardContent);
            
            log.info("发送飞书卡片成功: openId={}, tenantKey={}, cardId={}", 
                    openId, tenantKey, cardId);
            
        } catch (Exception e) {
            log.error("发送飞书卡片失败", e);
        }
    }
    
    /**
     * 通过系统用户ID获取绑定的第三方用户列表
     */
    private List<ThirdUser> getThirdUsersByUserId(Long userId) {
        try {
            // 1. 先通过ThirdUserAuth表找到关联的第三方用户ID
            LambdaQueryWrapper<ThirdUserAuth> authQuery = new LambdaQueryWrapper<>();
            authQuery.eq(ThirdUserAuth::getUserId, userId);
            List<ThirdUserAuth> authList = thirdUserAuthMapper.selectList(authQuery);
            
            if (authList.isEmpty()) {
                return new ArrayList<>();
            }
            
            // 2. 根据第三方用户ID查询第三方用户信息
            List<Long> thirdUserIds = authList.stream()
                    .map(ThirdUserAuth::getThirdUserId)
                    .collect(Collectors.toList());
            
            return thirdUserMapper.selectBatchIds(thirdUserIds);
            
        } catch (Exception e) {
            log.error("查询用户绑定的第三方账户失败: userId={}", userId, e);
            return new ArrayList<>();
        }
    }
    
    /**
     * 获取合同导出卡片模板ID
     */
    private String getContractExportTemplateId(String tenantKey, String templateKey) {
        try {
            FeiShuConfigProperties.FeiShuAppConfig config = feishuConfigStrategy.getConfigByTenantKey(tenantKey);
            if (config == null || config.getTemplates() == null) {
                return null;
            }
            
            FeiShuConfigProperties.FeiShuTemplateConfig templateConfig = config.getTemplates().get(templateKey);
            if (templateConfig == null) {
            return null;
            }
            
            return templateConfig.getTemplateId();
        } catch (Exception e) {
            log.warn("获取飞书卡片模板ID失败: tenantKey={}, templateKey={}", tenantKey, templateKey, e);
            return null;
        }
    }

    /**
     * 构建卡片内容
     */
    private String buildCardContent(String cardId, Integer contractNum, String fileName, String fileUrl) {
        // 检查是否为合同导出进度卡片（通过配置获取的模板ID）
        boolean isProgressCard = isContractExportProgressTemplate(cardId);
        boolean isCompleteCard = isContractExportCompleteTemplate(cardId);
        
        if (isProgressCard) {
            // 正在导出合同的卡片
            JSONObject cardContent = new JSONObject();
            cardContent.put("type", "template");
            
            JSONObject data = new JSONObject();
            data.put("template_id", cardId);
            
            JSONObject templateVariable = new JSONObject();
            templateVariable.put("contract_num", contractNum);
            
            data.put("template_variable", templateVariable);
            cardContent.put("data", data);
            
            return cardContent.toString();
            
        } else if (isCompleteCard) {
            // 导出完成的卡片，包含文件下载链接
            String safeName = fileName != null ? fileName : "合同导出文件";
            
            JSONObject cardContent = new JSONObject();
            cardContent.put("type", "template");
            
            JSONObject data = new JSONObject();
            data.put("template_id", cardId);
            
            JSONObject templateVariable = new JSONObject();
            templateVariable.put("contract_num", contractNum);
            templateVariable.put("file_name", safeName);
            
            // 构建飞书卡片URL格式的JSON对象
            JSONObject fileUrlJson = buildFeishuUrlJsonObject(fileUrl);
            templateVariable.put("file_url", fileUrlJson);
            
            data.put("template_variable", templateVariable);
            cardContent.put("data", data);
            
            return cardContent.toString();
        }
        
        // 默认返回简单文本消息
        JSONObject simpleMessage = new JSONObject();
        simpleMessage.put("text", "合同导出任务进度通知：" + contractNum + "个合同");
        return simpleMessage.toString();
    }
    
    /**
     * 构建飞书卡片URL格式的JSON对象（返回JSONObject）
     */
    private JSONObject buildFeishuUrlJsonObject(String url) {
        // 打印fileUrl日志
        log.info("合同导出 - 构建飞书卡片URL: 原始URL=[{}]", url);
        
        if (url == null || url.isEmpty()) {
            url = "https://open.feishu.cn";
            log.warn("合同导出 - fileUrl为空，使用默认URL: [{}]", url);
        }
        
        // 使用 JSONObject 来安全地构建JSON，避免手动转义的问题
        JSONObject urlJson = new JSONObject();
        urlJson.put("pc_url", url);
        urlJson.put("android_url", url);
        urlJson.put("ios_url", url);
        urlJson.put("url", url);
        
        log.info("合同导出 - 构建的飞书URL JSON: [{}]", urlJson.toString());
        
        return urlJson;
    }
    
    /**
     * 构建飞书卡片URL格式的JSON对象（返回字符串，保留兼容性）
     */
    private String buildFeishuUrlJson(String url) {
        return buildFeishuUrlJsonObject(url).toString();
    }
    
    /**
     * 检查是否为合同导出进度模板
     */
    private boolean isContractExportProgressTemplate(String templateId) {
        // 简化实现：只检查是否为合同导出相关的模板ID
        // 由于模板ID是从配置中动态获取的，这里主要做类型判断
        return templateId != null && (templateId.equals("AAqz0daF3QZ59") || 
                isTemplateIdInConfigs(templateId, "contract-export-progress"));
    }
    
    /**
     * 检查是否为合同导出完成模板
     */
    private boolean isContractExportCompleteTemplate(String templateId) {
        // 简化实现：只检查是否为合同导出相关的模板ID
        // 由于模板ID是从配置中动态获取的，这里主要做类型判断
        return templateId != null && (templateId.equals("AAqzjyO3N2JYn") || 
                isTemplateIdInConfigs(templateId, "contract-export-complete"));
    }
    
    /**
     * 检查模板ID是否在配置中
     */
    private boolean isTemplateIdInConfigs(String templateId, String templateKey) {
        try {
            // 这里可以添加更复杂的配置检查逻辑
            // 暂时简化处理，因为我们已经在获取模板ID时从配置读取
            return false;
        } catch (Exception e) {
            log.warn("检查模板ID配置失败: templateId={}, templateKey={}", templateId, templateKey, e);
            return false;
        }
    }
    
    /**
     * 发送卡片消息到飞书
     */
    private void sendCardMessage(FeiShuConfigProperties.FeiShuAppConfig config, String openId, String cardContent) {
        try {
            // 使用飞书SDK发送消息
            com.lark.oapi.Client client = com.lark.oapi.Client.newBuilder(config.getAppId(), config.getAppSecret()).build();
            
            // 创建请求对象
            com.lark.oapi.service.im.v1.model.CreateMessageReq req = com.lark.oapi.service.im.v1.model.CreateMessageReq.newBuilder()
                    .receiveIdType("open_id")
                    .createMessageReqBody(com.lark.oapi.service.im.v1.model.CreateMessageReqBody.newBuilder()
                            .receiveId(openId)
                            .msgType("interactive")
                            .content(cardContent)
                            .uuid(java.util.UUID.randomUUID().toString())
                            .build())
                    .build();
            
            // 发起请求
            com.lark.oapi.service.im.v1.model.CreateMessageResp resp = client.im().v1().message().create(req);
            
            // 处理服务端错误
            if (!resp.success()) {
                log.error("发送飞书卡片失败: code={}, msg={}, reqId={}", 
                        resp.getCode(), resp.getMsg(), resp.getRequestId());
            } else {
                log.info("发送飞书卡片成功: messageId={}", resp.getData().getMessageId());
            }
            
        } catch (Exception e) {
            log.error("调用飞书API发送卡片消息失败", e);
        }
    }
}